﻿using Microsoft.VisualStudio.Modeling;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Runtime.CompilerServices;

namespace Project.Co_DataTransformationPrototype
{
    public static partial class Operators
    {
        public partial class Operator
        {
            private bool checkCreateOperator<T>(IList<T> elementCollection)
            {
                Debug.Assert(opStore != null);

                // TODO: Implement precondition check   
                return true;
            }

            /// <summary>
            /// Wrapper function that creates an entity, provided the given parameters
            /// </summary>
            /// <param name="modelRoot"></param>
            /// <param name="entityName"></param>
            /// <param name="isImported"></param>
            /// <param name="attributeList"></param>
            /// <returns></returns>
            public Operator CreateEntityOp(ModelRoot modelRoot, string entityName, bool isImported, List<AttributeInfo> attributeList = null)
            {
                return CreateOperator(new List<EntityInfo> { new EntityInfo(modelRoot, entityName, isImported, attributeList) });
            }

            public Operator CreateEntityOp(ModelRoot modelRoot, string entityName, bool isImported, out System.Guid newElementID, List<AttributeInfo> attributeList = null)
            {
                return CreateOperator(new List<EntityInfo> { new EntityInfo(modelRoot, entityName, isImported, attributeList) }, out newElementID);
            }

            public Operator CreateAttributeOp(string attributeName, System.Data.SqlDbType type, string typeDescription, Entity parentEntity, System.Guid referencedEntityID, bool isPrimaryKey = false, bool isForeignKey = false)
            {
                return CreateOperator(new List<AttributeInfo> { new AttributeInfo(attributeName, type, typeDescription, parentEntity, referencedEntityID, isPrimaryKey, isForeignKey) });
            }

            public Operator CreateLinkOp(Entity sourceEntity, Entity targetEntity, string nameAttributeInSource, EntityAttribute attributeInTarget, bool isPrimaryKey = false, bool isForeignKey = false)
            {
                string nameAttributeInTarget = attributeInTarget.CurrentName;
                return CreateOperator(new List<ConnectorInfo> { new ConnectorInfo(sourceEntity, targetEntity, nameAttributeInSource, nameAttributeInTarget, attributeInTarget, isPrimaryKey, isForeignKey) });
            }

            public Operator CreateLinkOp(System.Guid sourceEntityID, Entity targetEntity, string nameAttributeInSource, EntityAttribute attributeInTarget, bool isPrimaryKey = false, bool isForeignKey = false)
            {
                Entity sourceEntity = getEntity(sourceEntityID);
                string nameAttributeInTarget = attributeInTarget.CurrentName;
                return CreateOperator(new List<ConnectorInfo> { new ConnectorInfo(sourceEntity, targetEntity, nameAttributeInSource, nameAttributeInTarget, attributeInTarget, isPrimaryKey, isForeignKey) });
            }

            public Operator CreateLinkOp(Entity sourceEntity, System.Guid targetEntityID, string nameAttributeInSource, EntityAttribute attributeInTarget, bool isPrimaryKey = false, bool isForeignKey = false)
            {
                Entity targetEntity = getEntity(targetEntityID);
                string nameAttributeInTarget = attributeInTarget.CurrentName;
                return CreateOperator(new List<ConnectorInfo> { new ConnectorInfo(sourceEntity, targetEntity, nameAttributeInSource, nameAttributeInTarget, attributeInTarget, isPrimaryKey, isForeignKey) });
            }

            /// <summary>
            /// Create operator.
            /// - Creates various types of model elements based on the provided input
            /// </summary>
            /// <typeparam name="T">Can be either Entity, EntityAttribute or EntityAssociation</typeparam>
            /// <param name="elementCollection"></param>
            /// <returns></returns>
            //[MethodImpl(MethodImplOptions.Synchronized)]
            public Operator CreateOperator<T>(IList<T> elementCollection)
            {
                checkCreateOperator(elementCollection);

                if (elementCollection != null)
                {
                    if (elementCollection.OfType<EntityInfo>().Count() > 0)
                        createEntity(elementCollection.OfType<EntityInfo>().ToList());
                    else if (elementCollection.OfType<AttributeInfo>().Count() > 0)
                        createAttributes(elementCollection.OfType<AttributeInfo>().ToList());
                    else if (elementCollection.OfType<ConnectorInfo>().Count() > 0)
                        createLink(elementCollection.OfType<ConnectorInfo>().ToList());
                }

                return this;
            }

            /// <summary>
            /// Create operator which returns the ID of the created element as an out parameter
            /// </summary>
            /// <typeparam name="T"></typeparam>
            /// <param name="elementCollection"></param>
            /// <param name="newElementID">out parameter</param>
            /// <returns></returns>
            //[MethodImpl(MethodImplOptions.Synchronized)]
            public Operator CreateOperator<T>(IList<T> elementCollection, out System.Guid newElementID)
            {
                checkCreateOperator(elementCollection);

                newElementID = System.Guid.Empty;
            
                if (elementCollection != null)
                {
                    if (elementCollection.OfType<EntityInfo>().Count() > 0)
                        newElementID = createEntity(elementCollection.OfType<EntityInfo>().ToList());
                    else if (elementCollection.OfType<AttributeInfo>().Count() > 0)
                        newElementID = createAttributes(elementCollection.OfType<AttributeInfo>().ToList());
                    else if (elementCollection.OfType<ConnectorInfo>().Count() > 0)
                        createLink(elementCollection.OfType<ConnectorInfo>().ToList());
                }

                return this;
            }

            //[MethodImpl( MethodImplOptions.Synchronized)]
            private System.Guid createEntity(IList<EntityInfo> entityList)
            {
                Entity entity = null;
                bool isCommited = false;

                foreach (EntityInfo eInfo in entityList)
                {
                    using (Transaction t = opStore.TransactionManager.BeginTransaction("Create Entity"))
                    {
                        entity = new Entity(opStore.DefaultPartition);
                        entity.ModelRoot = eInfo.modelRoot;
                        entity.Name = eInfo.name;
                        entity.IsImported = eInfo.isImported;
                         
                        t.Commit();
                        isCommited = true;
                    }

                    if (isCommited && eInfo.attributeList != null)
                    {
                        // Add its attributes
                        if (eInfo.attributeList.Count > 0)
                            createAttributes(eInfo.attributeList, entity);
                    }
                }

                return entity.Id;
            }

            private System.Guid createAttributes(IList<AttributeInfo> attributeList, Entity parent = null)
            {
                EntityAttribute entAttribute = null;

                using (Transaction t = opStore.TransactionManager.BeginTransaction("Create Attributes"))
                {
                    foreach (AttributeInfo attribute in attributeList)
                    {
                        Debug.Assert(attribute.parentEntity != null || parent != null);

                        entAttribute = new EntityAttribute(opStore.DefaultPartition);
                        entAttribute.Name = attribute.name;
                        entAttribute.Type = attribute.type;
                        entAttribute.isForeignKey = attribute.isForeignKey;
                        entAttribute.isPrimaryKey = attribute.isPrimaryKey;

                        if (attribute.isForeignKey)
                            entAttribute.ReferencedEntityID = attribute.referencedEntity;

                        if (parent != null)
                            parent.EntityAttributes.Add(entAttribute);
                        else
                            attribute.parentEntity.EntityAttributes.Add(entAttribute);
                    }
                    t.Commit();
                }

                return entAttribute.Id;
            }

            private void createLink(List<ConnectorInfo> connectorList)
            {
                using (Transaction t = opStore.TransactionManager.BeginTransaction("Create Connector"))
                {
                    foreach (ConnectorInfo link in connectorList)
                    {
                        //if (opStore.ElementDirectory.FindElements<EntityAssociation>().First(c => c.Source == link.source && c.Target == link.target) != null)
                        try
                        {
                            EntityAssociation rel = new EntityAssociation(link.source, link.target);
                            rel.AttributeInSource = link.nameAttributeInSource;
                            rel.AttributeInTarget = link.nameAttributeInTarget;
                            t.Commit();
                        }
                        catch (System.Exception e)
                        {
                            //System.Windows.Forms.MessageBox.Show(string.Format("Could not create a link. (error: {0})", e.Message), "Connector Validation", System.Windows.Forms.MessageBoxButtons.OK, System.Windows.Forms.MessageBoxIcon.Warning); 
                        }
                    }
                }
            }

        }
    }
}